package plugin

import (
	"context"
	"fmt"
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
	"strconv"
	"time"
)

type SqlTracePlugin struct {
}

func (plugin *SqlTracePlugin) Name() string {
	return "SqlTracePlugin"
}

func (plugin *SqlTracePlugin) Initialize(db *gorm.DB) (err error) {

	//TODO Before("*") After("*") 无法使用

	// 开始前
	db.Callback().Create().Before("gorm:before_create").Register("before", before)
	db.Callback().Query().Before("gorm:query").Register("before", before)
	db.Callback().Delete().Before("gorm:before_delete").Register("before", before)
	db.Callback().Update().Before("gorm:setup_reflect_value").Register("before", before)
	db.Callback().Row().Before("gorm:row").Register("before", before)
	db.Callback().Raw().Before("gorm:raw").Register("before", before)

	// 结束后
	db.Callback().Create().After("gorm:after_create").Register("after", after)
	db.Callback().Query().After("gorm:after_query").Register("after", after)
	db.Callback().Delete().After("gorm:after_delete").Register("after", after)
	db.Callback().Update().After("gorm:after_update").Register("after", after)
	db.Callback().Row().After("gorm:row").Register("after", after)
	db.Callback().Raw().After("gorm:raw").Register("after", after)

	return
}

func before(db *gorm.DB) {
	db.InstanceSet("startTime", time.Now())
	return
}

func after(db *gorm.DB) {
	var (
		ctx          context.Context
		start        interface{}
		isExist      bool
		sql          string
		startTime    time.Time
		ok           bool
		okContext    bool
		c            *gin.Context
		sqlInterface interface{}
		timeM        int64
	)

	ctx = db.Statement.Context
	if c, okContext = ctx.(*gin.Context); !okContext {
		fmt.Println("无法转换")
	}
	start, isExist = db.InstanceGet("startTime")
	if !isExist {
		return
	}

	startTime, ok = start.(time.Time)
	if !ok {
		return
	}

	//打印堆栈信息
	//fmt.Printf("答应s", debug.Stack())
	sql = db.Dialector.Explain(db.Statement.SQL.String(), db.Statement.Vars...)

	timeM = time.Since(startTime).Nanoseconds()
	//多个sql时拼接在一起
	if okContext {
		if sqlInterface, ok = c.Get("sql"); ok {
			c.Set("sql", sqlInterface.(string)+"; "+sql+strconv.Itoa(int(timeM)))
		} else {
			c.Set("sql", sql+" "+strconv.Itoa(int(timeM)))
		}

	}

	fmt.Println(time.Since(startTime).Nanoseconds(), "纳秒:", sql)
	return
}
